L.Canvas = L.Class.extend({
  includes: L.Mixin.Events,

  options: {
    width: 100,
    height: 100,
    scale: 1
  },

  initialize: function(element, room, options){
    L.Util.setOptions(this, options);

    // The Raphael context to draw on.
    this._raphael = Raphael(element, this.options.width, this.options.height);
    this._panzoom = this._raphael.panzoom({ initialZoom: 0, zoomStep:0.1, maxZoom:9, initialPosition:{x:this.options.width/2,y:this.options.height/2}});
    this._panzoom.enable();
    
    this._miniview = null;

    this.setRoom(room);
    this._elements = {};
    this._scale = this.options.scale;
    //referencia al nodo <svg>
    
    if ( options.svg )
    	this._raphael.canvas = $(options.svg)[0];
    this._$svgNode = $(this._raphael.canvas);
  },

  // Sketch scaling
  scale: function(value, center) {
    //center default true
    center = (typeof center === "undefined" || center === null) ? true : center;

    if(!value){
      return this._scale;
    }

    var width = this.options.width;
    var height = this.options.height;

    // set new width and height to paper
    var svgNode = this._$svgNode[0];
    svgNode.setAttribute('width', width*value);
    svgNode.setAttribute('height', height*value);
    svgNode.setAttribute('viewBox', [0, 0, width, height].join(' '));

    var left=0, top=0, dx=0, dy=0;
    var cx = width/2*(1-value);
    var cy = height/2*(1-value);
    if (center) {
      left = cx;
      top = cy;
    }
    
    if ( typeof center == "object" ) {
      x = (center.x + parseInt(svgNode.style.left))/this._scale;
      y = (center.y + parseInt(svgNode.style.top))/this._scale;
      console.log([x,y].join("-"));

      left = x*(1-value);
      top = y*(1-value);
    }
    
    svgNode.style.left = left;
    svgNode.style.top = top;
      
    // scale stroke width
    this._scale = value;
    this.redrawElements();
  },

  zoomIn: function(center){
    this._panzoom.currPos = center || {
      x: this.options.width/2,
      y: this.options.height/2
    };
    this._panzoom.zoomIn(1);
    this.updateBackgroundPanzoom();
  },

  zoomOut: function(center){
    this._panzoom.currPos = center || {
      x: this.options.width/2,
      y: this.options.height/2
    };
    this._panzoom.zoomOut(-1);
    this.updateBackgroundPanzoom();
  },
  
  updateBackgroundPanzoom: function() {
	  var pos = this._panzoom.getCurrentPosition();
	  var zoom = this._panzoom.getCurrentZoom();
	  var size = (zoom * 11) + 100;
	  $('#editor').css('background-size', size + '% ' + size + '%');
//	  console.log((pos.x - (this.options.width/2 * size/100.0)) + 'px ' + (pos.y - (this.options.height/2 * size/100.0)));
//	  $('#editor').css('background-position', (((this.options.width/2 * size/100.0) - pos.x) - this.options.width/2) + 'px ' + (((this.options.height/2 * size/100.0) - pos.y) - this.options.height/2) + 'px' );
	  if ( size > 100 )
		  $('#editor').css('background-position', (-pos.x)  + 'px ' + (-pos.y) + 'px' );
	  else
		  $('#editor').css('background-position', '0px 0px' );
  },

  redrawElements: function() {
    var self = this;
    var elements = this._elements;

    this._raphael.forEach(function (raphaelEl) {
      var id = raphaelEl.data('elementId');
      if ( elements.hasOwnProperty(id) ) {
        var el = elements[id];
        el.onUpdate(self);
      } else {
        raphaelEl.remove();
      }
    });
  },
 
  //metodos publicos de actualizacion de elementos
  addElement: function(element) {
    if ( this._elements.hasOwnProperty(element.id) ) {
      console.log('[WARNING] L.Canvas.addElement reemplazo un elemento, id:'+ element.id);
    }
    this._room.addCommand('addObject',{object:element.serialize()});
  },
  
  removeElement: function(elementId) {
    if (!this._elements.hasOwnProperty(elementId)) {
      console.log('[WARNING] L.Canvas.removeElement intento eliminar un elemento inexistente');
    }
    this._room.addCommand('removeObject',{objectId:elementId});
  },
  
  updateElement: function(elementId, data){
    if(!this._elements.hasOwnProperty(elementId)){
      console.log('[WARNING] L.Canvas.sendCommand.updateElement intento actualizar elemento inexistente');
    }
    this._room.addCommand('updateObject',{objectId:elementId, data:data});
  },
  
  clearElements: function(){
    this._room.addCommand('changeStore',{store:{}});
  },

  /* funciones relacionadas con el room asociado, Room dispara los eventos objectAction definidos aqui*/
  setRoom: function(room){
    if(this._room){
      this._room.off('objectAction', this.fireObjectAction, this);
      this._room.off('backgroundChange', this.fireBackgroundChange, this);
    }
    this._room = room;
    this._room.on('objectAction', this.fireObjectAction, this);
    this._room.on('backgroundChange', this.fireBackgroundChange, this);
    this._elements = {};
    this._room.eachObject(this.onAddObject, this);

  },

  fireObjectAction: function(params/*action_name,attr_list*/){
    switch(params.action_name) {
      case "addObject":
        this._onAddObject(params.attr_list);
        break;
      case "removeObject":
        this._onRemoveObject(params.attr_list);
        break;
      case "updateObject":
        this._onUpdateObject(params.attr_list);
        break;
      case "reloadObjects":
        this._onReloadObjects(params.attr_list);
        break;
    }
    this.fire('change', this);
  },
  
  fireBackgroundChange: function(background) {
	  $(this._raphael.canvas).parent().css('background-image',this._room.getBackgroundImageCss());
	  	if ( this._miniview )
	  		$(this._miniview).css('background-image',this._room.getBackgroundImageCss());
  },

  onAddObject: function(object) {
    var element = L.ElementFactory.getElement(object);
    this._elements[object.id] = element;
    element.onAdd(this);
  },

  _onAddObject: function(attr_list) {
//	console.log("_onAddObject");
    var object = attr_list.object;
    var element = L.ElementFactory.getElement(object);
    this._elements[object.id] = element;
    element.onAdd(this);
  },

  _onRemoveObject: function(attr_list){
    var objectId = attr_list.objectId;
    element = this._elements[objectId];
    element.onRemove(this);
    delete this._elements[objectId];
  },

  _onUpdateObject: function(attr_list){
    var object = attr_list.object;
    element = this._elements[object.id];
    element.attr(object);
  },

  _onReloadObjects: function(){
    this.eachElement(function(element){
      this._onRemoveObject({'objectId':element.id});
    }, this);
    this._room.eachObject(function(object){
      this._onAddObject({'object':object});
    }, this);
  },


  /* funciones relacionadas con el editor */
  onAdd: function (editor) {
    //guardar referencia al editor
    this._editor = editor;

    //attach events 
    //TODO: reemplazar por invoke
    this.eachElement(function(element){
      element.bindEvents();
    });

    this.defineEvents();

    var svgNode = this._$svgNode[0];

    svgNode.addEventListener('mousedown', this._onMousedown, false);
    svgNode.addEventListener('mousemove', this._onMousemove, false);
    svgNode.addEventListener('mouseup', this._onMouseup, false);
    svgNode.addEventListener('touchstart', this._onTouchstart, false);
    svgNode.addEventListener('touchmove', this._onTouchmove, false);
    svgNode.addEventListener('touchend', this._onTouchend, false);

    // Handle the case when the mouse is released outside the canvas.
    document.addEventListener('mouseup', this._onMouseup, false);
    document.addEventListener('touchend', this._onTouchend, false);    
  },

  onRemove: function(editor){
    //deattach events
    this.eachElement(function(element){
      element.unbindEvents();
    });

    var svgNode = this._$svgNode[0];

    svgNode.removeEventListener('mousedown', this._onMousedown, false);
    svgNode.removeEventListener('mousemove', this._onMousemove, false);
    svgNode.removeEventListener('mouseup', this._onMouseup, false);
    svgNode.removeEventListener('touchstart', this._onTouchstart, false);
    svgNode.removeEventListener('touchmove', this._onTouchmove, false);
    svgNode.removeEventListener('touchend', this._onTouchend, false);

    // Handle the case when the mouse is released outside the canvas.
    document.removeEventListener('mouseup', this._onMouseup, false);
    document.removeEventListener('touchend', this._onTouchend, false);    

    // eliminar referencia al editor
    this._editor = null;
  },

  defineEvents: function(){
    var self = this;
    
    //flag para evitar definer los listener mas de una vez
    if ( self._events_defined_ ) {
      return;
    } else {
      self._events_defined_ = true;
    }

    self._onMousedown = function _onMousedown(e){
      // Some feature detection to see which object to use
      e = e || window.event;
      var target = e.target || e.srcElement;

      //if(target instanceof SVGElement && target.nodeName != 'svg'){
        //self._editor.tool().onDragstartElement(L.Element.getFromNode(target), e);
      //  console.log("element_down")
      //}else{
        self._editor.tool().onDragstartCanvas(self, e);
      //}
    };

    self._onMousemove = function _onMousemove(e){
      // Some feature detection to see which object to use
      e = e || window.event;
      var target = e.target || e.srcElement;

      //if(target instanceof SVGElement && target.nodeName != 'svg'){
        //self._editor.tool().onDragmoveElement(L.Element.getFromNode(target), e);
      //  console.log("element_move")
      //}else{
        self._editor.tool().onDragmoveCanvas(self, e);
      //}
    }; 

    self._onMouseup = function _onMouseup(e){
      // Some feature detection to see which object to use
      e = e || window.event;
      var target = e.target || e.srcElement;

      //if(target instanceof SVGElement && target.nodeName != 'svg'){
        //self._editor.tool().onDragendElement(L.Element.getFromNode(target), e);
      //  console.log("element_up")
      //}else{
        self._editor.tool().onDragendCanvas(self, e);
      //}
    };

    self._onTouchstart = function _onTouchstart(e){
      e.preventDefault();

      if(e.touches.length == 1) {
        var touch = e.touches[0];
        self._onMousedown(touch);
      }
    };

    self._onTouchmove = function _onTouchmove(e){
      e.preventDefault();

      if (e.touches.length == 1) {
        var touch = e.touches[0];
        self._onMousemove(touch);
      }
    };

    self._onTouchend = function _onTouchend(e){
      self._onMouseup(e);
    };
  },

  editing: function() {
    return this._editor != null;
  },

  getPos: function(e){
    if(!this._editor) return;
    var svgNode = this.getSVGNode()[0];

    var uupos = svgNode.createSVGPoint();
        uupos.x = e.pageX;
        uupos.y = e.pageY;

    var ctm = svgNode.getScreenCTM();
    if (ctm = ctm.inverse()){
        uupos = uupos.matrixTransform(ctm); 
        return {
          x: uupos.x,
          y: uupos.y
        };      
    }
  },

  eachElement: function (fn, context) {
    var elements = this._elements;
    for (var i in elements) {
      if (elements.hasOwnProperty(i)) {
        fn.call(context, elements[i]);
      }
    }
    return this;
  },
  
  getElement: function(id){
    if(this._elements.hasOwnProperty(id)){
      return this._elements[id];
    }
    console.log("[WARNING] L.Canvas.getElement retorna un elemento null");
    return null;
  },

  getSVGNode: function(){
    return this._$svgNode;
  },
  
  getRoom: function(){
    return this._room;
  },

  getRaphael: function() {
    return this._raphael;
  },
  
  setMiniview: function(element) {
	  this._miniview = element;
  },
  
  getMiniview: function() {
	  return this._miniview;
  }

});